﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;

namespace Core.Experiment.Anim
{
    /// <summary>
    /// 基于Playable的简单动画控制器，
    /// 支持简单的动画播放，动画过渡功能
    /// 支持动画片段的动态添加与销毁
    /// 不支持BlendTree
    /// 支持动画分层（2023/2/8:新增动画分层，上下层weight变化为固定速率
    /// </summary>
    public partial class EasyAnimCtl : Component
    {
        public enum ELayer
        {
            Base,
            Up,
        }

        private PlayableGraph mGraph;

        private AnimationMixerPlayable[] mMixerArray;
        private AnimationLayerMixerPlayable mFinalMixer;

        private List<State>[] mState;

        private State mDefaultState;

        private float mUpLayerWeightChange;

        public readonly float TransitionTime = 0.2f;

        public Action<string> StartEvt;
        public Action<string> EndEvt;

        public void Create(Animator animator)
        {
            mGraph = PlayableGraph.Create(animator.name);
            mGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);

            mMixerArray = new AnimationMixerPlayable[2]; // 根据动画分层时的层数设置，现暂时只有上半身和下半身两层
            mMixerArray[(int)ELayer.Base] =
                AnimationMixerPlayable.Create(mGraph); // m_base_mixer = AnimationMixerPlayable.Create(m_graph);
            mMixerArray[(int)ELayer.Up] =
                AnimationMixerPlayable.Create(mGraph); // m_up_mixer = AnimationMixerPlayable.Create(m_graph);
            mFinalMixer = AnimationLayerMixerPlayable.Create(mGraph, mMixerArray.Length); // 输入：1)全身Mixer；2）上半身Mixer
            var output = AnimationPlayableOutput.Create(mGraph, "FinalAnim", animator);
            output.SetSourcePlayable(mFinalMixer);

            mGraph.Connect(mMixerArray[(int)ELayer.Base], 0, mFinalMixer, (int)ELayer.Base);
            mGraph.Connect(mMixerArray[(int)ELayer.Up], 0, mFinalMixer, (int)ELayer.Up);

            mFinalMixer.SetInputWeight((int)ELayer.Base, 1);
            mFinalMixer.SetInputWeight((int)ELayer.Up, 0);

            mState = new List<State>[mMixerArray.Length];
            for (int i = 0; i < mState.Length; ++i)
            {
                mState[i] = new List<State>();
            }

            mGraph.Play();
        }

        public void Update()
        {
            var weight = mFinalMixer.GetInputWeight((int)ELayer.Up);
            weight = Mathf.Clamp(weight + mUpLayerWeightChange, 0, 1);
            mFinalMixer.SetInputWeight((int)ELayer.Up, weight);

            for (int layer = 0; layer < mState.Length; ++layer)
            {
                var totalWeight = 0f;
                for (int i = 0; i < mState[layer].Count; i++)
                {
                    var state = mState[layer][i];
                    state.Update();

                    if (state.IsStateOut)
                    {
                        totalWeight += state.Weight;
                        //Debug.LogError($"anim_name={state.AnimName} weight={state.Weight}, layer={layer}");
                    }

                    mMixerArray[layer].SetInputWeight(i, state.Weight);
                }

                for (int i = 0; i < mState[layer].Count; i++)
                {
                    var state = mState[layer][i];

                    if (!state.IsStateIn) continue;

                    state.SetWeight(1 - totalWeight);
                    //Debug.LogError($"anim_name={state.AnimName} weight={state.Weight}, layer={layer}");
                    mMixerArray[layer].SetInputWeight(i, state.Weight);
                    break;
                }
            }

            if (Input.GetKeyDown(KeyCode.D))
            {
                for (var i = 0; i < mState[0].Count; i++)
                {
                    mMixerArray[(int)ELayer.Base].SetInputWeight(i, mState[0][i].Weight);
                }
            }

            if (Input.GetKeyDown(KeyCode.Space))
            {
                for (int i = 0; i < mState[0].Count; i++)
                {
                    log.d(
                        $"anim_name={mState[0][i].AnimName} weight={mMixerArray[(int)ELayer.Base].GetInputWeight(i)}");
                }
            }
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            foreach (var lstState in mState)
            {
                foreach (var item in lstState)
                {
                    item.Dispose();
                }

                lstState.Clear();
            }

            mDefaultState = null;
            StartEvt = null;
            EndEvt = null;

            if (mGraph.IsValid())
            {
                mGraph.Destroy();
            }
        }

        public void SetUpLayerAvatarMask(AvatarMask mask)
        {
            mFinalMixer.SetLayerMaskFromAvatarMask((int)ELayer.Up, mask);
        }

        private void SetDefaultState(State state)
        {
            // 只应用于Base层默认动作
            if (state == null || state == mDefaultState)
            {
                return;
            }

            mDefaultState?.SetDefault(false);
            state.SetDefault(true);
            mDefaultState = state;
        }

        public void Play(string animPath, string animName, AnimationClip clip, bool isDefault, int layer, bool isFade,
            float time = 0, Action startCompleteFunc = null)
        {
            if (mGraph.IsValid() == false)
            {
                Debug.LogError("PlayableGraph is Invalid");
                return;
            }

            var state = mState[layer].Find(c => c.AnimName == animName);

            mState[layer].ForEach(c => c.StateOut(isFade));

            if (layer == (int)ELayer.Up)
            {
                mUpLayerWeightChange = Time.time / 2;
            }

            bool setBaseDefault = isDefault && layer == (int)ELayer.Base;
            if (state != null)
            {
                if (setBaseDefault)
                {
                    SetDefaultState(state);
                }

                state.Play(true, time);
                return;
            }

            state = G.Factory.Create<State>();

            state.New(animPath, animName, clip, layer);
            state.Attach(this);

            state.StartCallBackEvt += startCompleteFunc;
            state.StartEvt += OnStartEvtHandler;
            state.EndEvt += OnEndEvtHandler;

            mState[layer].Add(state);

            if (setBaseDefault)
            {
                SetDefaultState(state);
            }

            state.Play(isFade, time);
        }

        public void Pause(string animName, float time, int layer)
        {
            var state = mState[layer].Find(c => c.AnimName == animName);
            if (state == null)
            {
                return;
            }

            state.Pause(time);
        }

        private void OnStartEvtHandler(State state)
        {
            StartEvt?.Invoke(state.AnimName);
        }

        private void OnEndEvtHandler(State state)
        {
            EndEvt?.Invoke(state.AnimName);

            if (state.Layer == ELayer.Up)
            {
                mUpLayerWeightChange = -Mathf.Abs(mUpLayerWeightChange);
                mState[(int)ELayer.Up].ForEach(c => c.StateOut());
            }
            else if (mDefaultState != null && state != mDefaultState)
            {
                bool isAllEnd = true;
                int layer = (int)ELayer.Base;
                for (int i = 0; i < mState[layer].Count; i++)
                {
                    if (mState[layer][i] == mDefaultState)
                    {
                        continue;
                    }

                    if (mState[layer][i].IsPlaying)
                    {
                        isAllEnd = false;
                        break;
                    }
                }

                if (isAllEnd)
                {
                    mState[layer].ForEach(c => c.StateOut());

                    mDefaultState.Play();
                }
            }
        }
    }
}
